﻿using InternetSpiderLib;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;

namespace InstanceGenerator
{
    public class XmlInstanceGenerator
    {
        /// <summary>
        /// 根据XML描述生成对象
        /// </summary>
        /// <param name="document"></param>
        /// <returns></returns>
        public static IEnumerable<object> Generator(XmlDocument document)
        {
            var dlls = document.SelectNodes("NetSpider/Dependent/Item");
            foreach (XmlNode item in dlls)
            {
                var fileName = AssemblyHelper.GetFullPath(item.InnerText);
                Assembly.LoadFrom(fileName);
            }

            Dictionary<string, string> mapping = new Dictionary<string, string>();
            var variables = document.SelectNodes("NetSpider/SpiderVariable/Item");
            foreach (XmlNode item in variables)
            {
                var name = item.SelectSingleNode("./Name").InnerText;
                var value = item.SelectSingleNode("./Value").InnerText;
                if (string.IsNullOrEmpty(name) == false)
                    mapping[name] = value;
            }

            List<object> objects = new List<object>();

            var items = document.SelectNodes("NetSpider/Spiders/Item");
            foreach (XmlNode item in items)
            {
                var obj = Generator(item, mapping);
                if (obj != null)
                    objects.Add(obj);
            }

            return objects;
        }

        /// <summary>
        /// 生成对象
        /// </summary>
        /// <param name="node"></param>
        /// <param name="mapping"></param>
        /// <returns></returns>
        internal static object Generator(XmlNode node, Dictionary<string, string> mapping = null)
        {
            var typeFullName = node.SelectSingleNode("./Type")?.InnerText;

            if (string.IsNullOrEmpty(typeFullName))
                return null;

            Type type = Type.GetType(typeFullName);

            if (type == null)
            {
                Console.WriteLine($"找不到类型 {typeFullName}");
                return null;
            }


            if (type.IsAssignableFrom(typeof(string)))
            {
                return FormatValue(node.SelectSingleNode("./Value")?.InnerText, mapping);
            }

            if (type.IsEnum)
            {
                var value = node.SelectSingleNode("./Value")?.InnerText;
                return Enum.Parse(type, value);
            }

            if (type.IsValueType)
            {
                var value = node.SelectSingleNode("./Value")?.InnerText;
                if (string.IsNullOrEmpty(value))
                    return Activator.CreateInstance(type);

                return Convert.ChangeType(FormatValue(value, mapping), type);
            }

            if (type.IsArray)
            {
                var subType = type.GetElementType();
                if (subType == null)
                    return null;

                var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(subType));

                var itemsNodes = node.SelectNodes("./List/Item");
                if (itemsNodes != null)
                {
                    foreach (XmlNode item in itemsNodes)
                    {
                        var itemInlist = Generator(item, mapping);
                        if (itemInlist != null)
                            list.Add(itemInlist);
                    }
                }

                Array array = Array.CreateInstance(subType, list.Count);
                list.CopyTo(array, 0);
                return array;
            }

            var instance = Activator.CreateInstance(type);
            var items = node.SelectNodes("./Property/Item");
            if (items != null)
            {
                foreach (XmlNode item in items)
                {
                    var name = item.SelectSingleNode("./Name")?.InnerText;
                    if (string.IsNullOrEmpty(name))
                        continue;

                    var property = type.GetProperty(name);
                    if (property == null)
                        continue;

                    var propertyItem = Generator(item, mapping);
                    if (propertyItem == null)
                        continue;

                    if (property.PropertyType.IsAssignableFrom(propertyItem.GetType()) == false)
                        continue;

                    property.SetValue(instance, propertyItem);
                }
            }

            return instance;
        }

        private static string FormatValue(string value, Dictionary<string, string> mapping)
        {
            if (string.IsNullOrEmpty(value))
                return value;

            if (mapping == null)
                return value;

            foreach (var item in mapping)
            {
                value = value.Replace($"{{Config:{item.Key}}}", item.Value);
            }

            return value;
        }
    }
}
